import * as zh from 'blockly/msg/zh-hans';
import * as zh_hant from 'blockly/msg/zh-hant';
import * as en from 'blockly/msg/en';
import { CustomBlocks } from '@/settings/blocks';
import * as Blockly from 'blockly/core';
import DarkTheme from '@blockly/theme-dark';
import { javascriptGenerator } from 'blockly/javascript';
import { ComUtils } from '@/utils/Com';
import { EditorState } from "@codemirror/state"
import { EditorView, basicSetup } from "codemirror"
import { javascript } from "@codemirror/lang-javascript"
import { oneDark } from "@codemirror/theme-one-dark";
import { TextEditorConfig } from '@/settings/config/text-editor';
import { MessageBox } from '@/utils/messagebox';
import '@/assets/css/editor.css'
import { DogEditor } from '@/utils/dog-editor';
import { getLines, getValue } from '@/settings/blocks/com';
import { jsHelperExtension } from '@/settings/config/code-runtime';

enum EditorType {
    Blockly,
    Text
}

let workspace: Blockly.WorkspaceSvg | EditorView
const box = document.getElementById('editor') as HTMLDivElement
const project = {} as ProjectItem

window.dogEditor = {
    usePlugin: DogEditor.usePlugin,
    getValue,
    getLines,
    setEditorDoc,
    setCache,
    getCache,
    openLink,
    programmingType:'blockly' as ProjectItemProgrammingType
}

DogEditor.init()
window.addEventListener('message', handler)

function handler(event: any) {
    switch (event.data.type) {
        case 'save-project':
            saveProject();
            break;
        case 'set-project':
            setProject(event.data.data.projoct, event.data.data.plugins);
            break;
        case 'run-project':
            saveAndRunProject();
            break;
        case 'change-editor':
            changeEditor(event.data.data);
            break;
        case 'clear-editor':
            clearEditor();
            break
    }
}

function setCache(key: string, value: string) {
    DogEditor.callHostFunc('setCache', key, value)
}

function getCache(key: string) {
    return DogEditor.callHostFunc<string>('getCache', key)
}

function openLink(url: string) {
    DogEditor.callHostFunc('openLink', url)
}

function saveProject(run = false) {
    const data = project
    data.updateTime = Date.now()
    if (workspace instanceof Blockly.WorkspaceSvg) {
        try {
            const code = javascriptGenerator.workspaceToCode(workspace);
            data.code = TextEditorConfig.getEmptyTemplate(code)
        } catch (error: any) {
            workspace.highlightBlock(error.message)
            MessageBox.error(ComUtils.tr('args_error'))
            console.log(error)
            return
        }
        const state = Blockly.serialization.workspaces.save(workspace)
        data.source = JSON.stringify(state)

    } else if (workspace instanceof EditorView) {
        data.code = workspace.state.doc.toString()
    }

    const encoder = new TextEncoder()
    data.size = encoder.encode(data.code + data.source).length

    if (run) {
        window.parent?.postMessage({ type: 'run-project', data }, '*')
    } else {
        window.parent?.postMessage({ type: 'update-project', data }, '*')
    }

}

async function setProject(projoct: ProjectItem, plugins: string[]) {
    window.dogEditor.programmingType = projoct.programmingType
    clearEditor()
    Object.assign(project, projoct)
    initPlugins(plugins)
    if (project.programmingType === 'text') {
        initTextEditor()
    } else {
        initBlocklyEditor()
    }
}

function saveAndRunProject() {
    saveProject(true)
}

function initPlugins(plugins: string[]) {
    if(plugins.length === 0){
        return
    }

    const temp = plugins.map((item) => {
        return `(function () { \n ${item} \n })();`
    })
    
    const code = temp.join('\n')
    if (code.trim().length === 0) {
        return
    }
    const old = document.getElementById('plugin')
    if (old) {
        old.remove()
    }
    const pluginDom = document.createElement('script')
    pluginDom.innerHTML = code
    pluginDom.id = 'plugin'
    document.body.appendChild(pluginDom)
}

const clearEditor = () => {
    if (workspace instanceof Blockly.WorkspaceSvg) {
        workspace.dispose()
    } else if (workspace instanceof EditorView) {
        workspace.destroy()
    }
}

async function changeEditor(editorType: EditorType) {
    clearEditor()
    if (editorType === EditorType.Blockly) {
        initBlocklyEditor()
    } else {
        initTextEditor()
    }
}

function loadProject() {
    window.parent.postMessage({ type: 'get-project', data: '' }, '*')
}

async function initTextEditor() {
    let code = project.code.trim();
    if (code.length === 0 && project.type !== 'editorPlugin') {
        code = TextEditorConfig.getEmptyTemplate()
    }
    workspace = new EditorView({
        state: EditorState.create({
            doc: code,
            extensions: [
                basicSetup,
                jsHelperExtension,
                javascript(),
                oneDark,
            ],
        }),
        parent: box,
    })
}

function setEditorDoc(doc: string) {
    if (workspace instanceof EditorView) {
        workspace.dispatch({
            changes: {
                from: 0,
                to: workspace.state.doc.length,
                insert: doc
            }
        })
    }

}

async function initBlocklyEditor() {
    DogEditor.initGenerator()
    const coreBlocks = CustomBlocks.getCoreBlocks()
    workspace = Blockly.inject(box, {
        toolbox: {
            contents: coreBlocks
        },
        zoom: {
            controls: true,
            wheel: true,
            startScale: 1.0,
            maxScale: 3,
            minScale: 0.3,
            scaleSpeed: 1.2,
            pinch: true
        },
        theme: DarkTheme,
        media: ComUtils.getResourceUrl('/blockly/media'),
    });
    workspace.addChangeListener(() => {
        (workspace as Blockly.WorkspaceSvg).highlightBlock(null)
    })

    if (project.source.trim().length === 0) {
        return
    }

    try {
        const state = JSON.parse(project.source)
        Blockly.serialization.workspaces.load(state, workspace)
    } catch (error) {
        console.log('load source error', error)
        MessageBox.error(ComUtils.tr('parse_source_error'))
    }
}

async function init() {
    Blockly.setLocale(en as any);
    await CustomBlocks.setCustonLocale('en');
    const lang = navigator.language
    if (lang === 'zh-CN') {
        Blockly.setLocale(zh as any);
        await CustomBlocks.setCustonLocale('zh-CN');
    } else if (lang === 'zh-TW') {
        Blockly.setLocale(zh_hant as any);
        await CustomBlocks.setCustonLocale('zh-TW');
    }
    console.log('init editor')
    CustomBlocks.init()
    loadProject()
}

init()
